﻿using System;

namespace Grimgine.Math
{
    /// <summary>
    /// 
    /// </summary>
    public enum PlaneStatus
    {
        /// <summary>
        /// 
        /// </summary>
        Plane_Back = -1,

        /// <summary>
        /// 
        /// </summary>
        Plane_On,

        /// <summary>
        /// 
        /// </summary>
        Plane_Front
    }

    /// <summary>
    /// 
    /// </summary>
    public struct Plane : IEquatable<Plane>
    {
        /// <summary>
        /// 
        /// </summary>
        public float A, B, C, D;

        /// <summary>
        /// 
        /// </summary>
        public Plane(float a, float b, float c, float d)
        {
            A = a; B = b; C = c; D = d;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="t1"></param>
        /// <param name="t2"></param>
        /// <param name="t3"></param>
        public void Create(Vector3 t1, Vector3 t2, Vector3 t3)
        {
            Vector3 e1, e2, n;

            e1 = t2 - t1;
            e2 = t3 - t1;

            n = Vector3.Cross(e1, e2);

            A = n.X; B = n.Y; C = n.Z;
            D = -(A * t1.X + B * t1.Y + C * t1.Z);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        public float Distance(float x, float y, float z)
        {
            return (A * x + B * y + C * z + D);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="v1"></param>
        /// <returns></returns>
        public float Distance(Vector3 v1)
        {
            return Distance(v1.X, v1.Y, v1.Z);
        }

        /// <summary>
        /// 
        /// </summary>
        public void Normalize()
        {
            this = Normalize(this);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="p1"></param>
        /// <returns></returns>
        public static Plane Normalize(Plane p1)
        {
            float lensq = p1.A * p1.A + p1.B * p1.B + p1.C * p1.C;
            float recip = 1 / (float)System.Math.Sqrt(lensq);

            Plane p2;
            p2.A = p1.A * recip;
            p2.B = p1.B * recip;
            p2.C = p1.C * recip;
            p2.D = p1.D * recip;

            return p2;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <returns></returns>
        public static bool operator ==(Plane p1, Plane p2)
        {
            return (p1.A == p2.A && p1.B == p2.B && p1.C == p2.C && p1.D == p2.D);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <returns></returns>
        public static bool operator !=(Plane p1, Plane p2)
        {
            return !(p1 == p2);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            return obj is Plane && this == (Plane)obj;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public bool Equals(Plane p)
        {
            return (this == p);
        }
    }
}
